<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>vue</title>
</head>

<body>
  <!-- 2.挂载点 -->
  <div id="app">
    <h3 v-html="msg"></h3>
    <h3 v-text="msg"></h3>
    <div>
      <h1>{{msg}}</h1>
    </div>
    <input type="text" v-model="msg">
  </div>

  <script>
    let data = {
      msg: '<div>你好 mvvm</div>',
      info: { user: 'lisi' },
      info_user: 'lisi'
    }
    const compileMethod = {
      html(node, data, key) {
        node.innerHTML = data[key]
      },
      text(node, data, key) {
        node.innerText = data[key]
      },
      // v-model它就是一个语法糖，它就是 value和oninput事件的简写
      model(node, data, key) {
        // 输入事件
        node.addEventListener('input', e => {
          data[key] = e.target.value
          node.value = e.target.value
        })
      }
    }

    let el = document.querySelector('#app')
    // 数据代理
    observe(data)

    // 模板解析
    compile(el, data)

    // 数据响应
    function observe(data) {
      if (typeof data != 'object' || data == null) return;

      // 只循环第1层,如果有多个层级，就需要递归了
      Object.keys(data).forEach(key => {
        reactive(data, key, data[key])
      })
    }
    function reactive(data, key, value) {
      // 递归完成数据的监听  此处就是vue中的性能较低的原因之一
      // 优化的一原则：data中定义的数据尽可能的扁平化 
      observe(value)

      let dep = new Dep()

      Object.defineProperty(data, key, {
        get() {
          // console.log('get', key)
          Dep.target && dep.add(Dep.target)
          return value
        },
        set(v) {
          if (v != value) {
            // console.log('set', key)
            // 修改源数据
            value = v
            // 通过视图更新
            document.querySelector('[v-html]').innerHTML = v
            // notify
            dep.notify()
          }
        }
      })
    }



    // 模板编译
    function compile(rootDom, data) {
      // 查询根节点下面的子节点
      let childs = rootDom.childNodes
      // 循环遍历子元素
      childs.forEach(node => {
        if (node.nodeType == 1) {
          // 元素节点
          // console.log(node.nodeName)
          compilerElement(node, data)
          // 元素节点它还有可能有子节点
          if (node.childNodes.length > 0) compile(node, data)
        } else if (node.nodeType == 3 && /\{\{\s*(\w+)\s*\}\}/.test(node.nodeValue)) { //{{ msg }}
          // 文本节点
          node.nodeValue = data[RegExp.$1]
        }
      })
    }

    function compilerElement(node, data) {
      // 获取当前dom中所有的属性
      // 它是一个map对象,转换成为数组，方便迭代
      const attrs = [...node.attributes]
      if (attrs.length > 0) {
        attrs.forEach(({ name, value }) => {
          // 只要v-开头的属性，它才是我们要去绑定或事件处理的属性  指令
          if (name.startsWith('v-')) {
            // node.innerHTML = data[value]
            compileMethod[name.slice(2)](node, data, value)
          }
        })
      }
    }

    // 数据的更新视图操作，如果发布订阅模式完成 统一
    // 观察者
    class Watcher {
      constructor(vue, key) {
        Dep.target = this
        vue[key]
        Dep.target = null
      }

      update() {
        // 更新操作
      }

    }

    // 收集观察依赖类
    class Dep {
      // 所有观察对象
      deps = []

      add(watcher) {
        this.deps.push(watcher)
      }

      notify() {
        this.deps.forEach(watcher => watcher.update())
      }

    }












  </script>

</body>

</html>